using Microsoft.Extensions.Caching.Distributed;
using System;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;

namespace TK.Common.Cache
{
    /// <summary>
    /// 分布式缓存扩展
    /// </summary>
    public static class DistributedCacheExtensions
    {
        /// <summary>
        /// Gets a value with the given key.
        /// </summary>
        public static async Task<T> GetAsync<T>(this IDistributedCache cache, string key, CancellationToken token = default)
        {
            var cacheTxt = await cache.GetStringAsync(key, token);
            if (!string.IsNullOrWhiteSpace(cacheTxt))
            {
                return JsonSerializer.Deserialize<T>(cacheTxt);
            }

            return default;
        }

        /// <summary>
        ///  Sets the value with the given key.
        /// </summary>
        public static async Task SetAsync<T>(this IDistributedCache cache, string key, T value, DistributedCacheEntryOptions options, CancellationToken token = default)
        {
            var cacheTxt = value != null ? JsonSerializer.Serialize(value) : null;
            await cache.SetStringAsync(key, cacheTxt, options, token);
        }


        /// <summary>
        ///  Gets a value with the given key;key not exists return func
        /// </summary>
        public static async Task<TResult> GetAsync<TResult>(this IDistributedCache cache, string key, Func<Task<TResult>> func, TimeSpan? expire = null, CancellationToken token = default)
        {
            var value = await cache.GetStringAsync(key, token);
            if (!string.IsNullOrWhiteSpace(value))
            {
                return JsonSerializer.Deserialize<TResult>(value);
            }

            if (func != null)
            {
                var obj = await func();
                if (obj != null)
                {
                    await cache.SetStringAsync(key, JsonSerializer.Serialize(obj), new DistributedCacheEntryOptions { SlidingExpiration = expire ?? new TimeSpan(24, 0, 0) }, default);
                }

                return obj;
            }

            return default;
        }
    }
}
